

#include <stdlib.h>
#include <sstream>
#include "catpeople.h"
#include "exppeople.h"



using namespace std;



// CatPerson ::



CatPerson::CatPerson()
{
}



CatPerson::CatPerson( Subject *person )
{
    m_Subjects.push_back( person );    
}



bool CatPerson::empty(Subject *owner)
{
    return false;    
}



bool CatPerson::one(Subject *owner)
{
    return true;
}



uint8_t CatPerson::getCount(Subject *owner)
{
    return 1;
}
    


void CatPerson::generate( CollectionSubjects &subs, Subject *owner, uint8_t level )
{
    // this category generates expressions only if the owner is not the subject
    if( *getSubjects(owner).begin() != owner )
    {
        if( ExpAllTellTruth::complexity() <= level )
            owner->addExpression( ExpAllTellTruth::createExpression( this, owner ) );
        
        if( ExpAllHumans::complexity() <= level )
            owner->addExpression( ExpAllHumans::createExpression(this, owner) );
        
        if( ExpAllWizards::complexity() <= level )
            owner->addExpression( ExpAllWizards::createExpression(this, owner) );
        
        if( ExpAllVampires::complexity() <= level )
            owner->addExpression( ExpAllVampires::createExpression(this, owner) );

        if( ExpName::complexity() <= level )
        {
            CollectionSubjects::iterator ip = subs.begin(), ip_end = subs.end();
            for( ; ip != ip_end; ++ip )
            {
                Person *person = static_cast <Person*> (*ip);
                owner->addExpression( ExpName::createExpression( this, owner, person->getName() ) );
            }
        }
    }
}



// CatThisPerson ::



CatThisPerson::CatThisPerson()
    : CatPerson()
{
    m_Text = "I";
}



Category *CatThisPerson::createCategory()
{
    return new CatThisPerson();
}



bool CatThisPerson::personal() const
{
    return true;
}



const CollectionSubjects &CatThisPerson::getSubjects( Subject *owner )
{
    if( owner )
    {
        m_Subjects.clear();
        m_Subjects.push_back(owner);
    }
    return m_Subjects;    
}



void CatThisPerson::generate( CollectionSubjects &subs, Subject *owner, uint8_t level )
{
    if( ExpAllTellTruth::complexity() <= level )
        owner->addExpression( ExpAllTellTruth::createExpression( this, owner ) );
    
    if( ExpAllHumans::complexity() <= level )
        owner->addExpression( ExpAllHumans::createExpression(this, owner) );
        
    if( ExpAllWizards::complexity() <= level )
        owner->addExpression( ExpAllWizards::createExpression(this, owner) );
    
    if( ExpAllVampires::complexity() <= level )
        owner->addExpression( ExpAllVampires::createExpression(this, owner) );

    if( ExpName::complexity() <= level )
    {
        CollectionSubjects::iterator ip = subs.begin(), ip_end = subs.end();
        for( ; ip != ip_end; ++ip )
        {
            Person *person = static_cast <Person*> (*ip);
            owner->addExpression( ExpName::createExpression( this, owner, person->getName() ) );
        }
    }
}



// CatTheLeftPerson ::



CatTheLeftPerson::CatTheLeftPerson( Subject *person )
    : CatPerson(person)
{
    m_Text = "this guy on the left";
}



Category *CatTheLeftPerson::createCategory( CollectionSubjects &people )
{
    if( people.size() > 1 )
    {
        Subject *person = *people.begin();
        return new CatTheLeftPerson(person);
    }
    else
        return NULL;
}
    
    

// CatTheRightPerson ::



CatTheRightPerson::CatTheRightPerson( Subject *person )
    : CatTheLeftPerson(person)
{
    m_Text = "this guy on the right";    
}



Category *CatTheRightPerson::createCategory( CollectionSubjects &people )
{
    if( people.size() > 1 )
    {
        Subject *person = *--people.end();
        return new CatTheRightPerson(person);
    }
    else
        return NULL;
}



// CatMiddlePerson ::



CatMiddlePerson::CatMiddlePerson( Subject *person )
    : CatTheLeftPerson(person)
{
    m_Text = "this guy in the middle";
}



Category *CatMiddlePerson::createCategory( CollectionSubjects &people )
{
    if( people.size() == 3 )
    {
        Subject *person = *++people.begin();
        return new CatMiddlePerson(person);
    }
    else
        return NULL;
}



// CatColorPeople ::



CatColorPeople::CatColorPeople( CollectionSubjects &people, Color::eColor col )
    : CatColor( people, col )
{
    ostringstream oss;
    oss << (one() ? "the guy dressed in " : "the guys dressed in ") << m_Color.getText();
    m_Text = oss.str();
}



Category *CatColorPeople::createCategory( CollectionSubjects &people, Color::eColor col, Color::eColor max_col )
{
    // try creating the collection
    Category *cat = new CatColorPeople( people, col );
    if( cat->empty() || cat->getCount() == people.size() )
    {
        delete cat;
        return NULL;
    }
    else
        return cat;     
}



void CatColorPeople::generate( CollectionSubjects &subs, Subject *owner, uint8_t level )
{
    if( ExpAllTellTruth::complexity() <= level )
        owner->addExpression( ExpAllTellTruth::createExpression( this, owner ) );
        
    if( ExpAllLie::complexity() <= level )
        owner->addExpression( ExpAllLie::createExpression(this, owner) );
    
    if( ExpAllHumans::complexity() <= level )
        owner->addExpression( ExpAllHumans::createExpression(this, owner) );
        
    if( ExpAllWizards::complexity() <= level )
        owner->addExpression( ExpAllWizards::createExpression(this, owner) );
    
    if( ExpAllVampires::complexity() <= level )
        owner->addExpression( ExpAllVampires::createExpression(this, owner) );
    
    if( ExpSomeHumans::complexity() <= level )
        owner->addExpression( ExpSomeHumans::createExpression(this, owner) );
        
    if( ExpSomeWizards::complexity() <= level )
        owner->addExpression( ExpSomeWizards::createExpression(this, owner) );
    
    if( ExpSomeVampires::complexity() <= level )
        owner->addExpression( ExpSomeVampires::createExpression(this, owner) );
    
    if( ExpName::complexity() <= level )
    {
        CollectionSubjects::iterator ip = subs.begin(), ip_end = subs.end();
        for( ; ip != ip_end; ++ip )
        {
            Person *person = static_cast <Person*> (*ip);
            owner->addExpression( ExpName::createExpression( this, owner, person->getName() ) );
        }
    }
}



// CatNeighbours ::



CatNeighbours::CatNeighbours( CollectionSubjects &people )
    : m_pBaseSubjects(&people)
    , m_pLastOwner(NULL)
{
}



Category *CatNeighbours::createCategory( CollectionSubjects &people )
{
    if( people.size() > 1 )
        return new CatNeighbours(people);
    else
        return NULL;
}



bool CatNeighbours::one( Subject *owner )
{
    refill(owner);
    return CatCollection::one();
}



const string &CatNeighbours::putText( Subject *owner )
{
    m_Text = (one(owner) ? "my neighbour" : "my neighbours");
    return m_Text;
}



const CollectionSubjects &CatNeighbours::getSubjects( Subject *owner )
{
    refill(owner);
    return m_Subjects;
}



void CatNeighbours::refill( Subject *owner )
{
    if( owner != m_pLastOwner )
    {
        m_Subjects.clear();
        CollectionSubjects::iterator ip = m_pBaseSubjects->begin(), ip_end = m_pBaseSubjects->end(), ip_prev = m_pBaseSubjects->end();
        for( ; ip != ip_end; ++ip )
        {
            if( *ip == owner )
            {
                if( ip_prev != ip_end )
                    m_Subjects.push_back(*ip_prev);
                if( ++ip  != ip_end )
                    m_Subjects.push_back(*ip);
                break;
            }
            ip_prev = ip;
        }
        m_pLastOwner = owner;
    }
}



void CatNeighbours::generate( CollectionSubjects &subs, Subject *owner, uint8_t level )
{
    if( ExpAllTellTruth::complexity() <= level )
        owner->addExpression( ExpAllTellTruth::createExpression( this, owner ) );
        
    if( ExpAllLie::complexity() <= level )
        owner->addExpression( ExpAllLie::createExpression(this, owner) );
    
    if( ExpAllHumans::complexity() <= level )
        owner->addExpression( ExpAllHumans::createExpression(this, owner) );
        
    if( ExpAllWizards::complexity() <= level )
        owner->addExpression( ExpAllWizards::createExpression(this, owner) );
    
    if( ExpAllVampires::complexity() <= level )
        owner->addExpression( ExpAllVampires::createExpression(this, owner) );
    
    if( ExpSomeHumans::complexity() <= level )
        owner->addExpression( ExpSomeHumans::createExpression(this, owner) );
        
    if( ExpSomeWizards::complexity() <= level )
        owner->addExpression( ExpSomeWizards::createExpression(this, owner) );
    
    if( ExpSomeVampires::complexity() <= level )
        owner->addExpression( ExpSomeVampires::createExpression(this, owner) );

    if( ExpName::complexity() <= level )
    {
        CollectionSubjects::iterator ip = subs.begin(), ip_end = subs.end();
        for( ; ip != ip_end; ++ip )
        {
            Person *person = static_cast <Person*> (*ip);
            owner->addExpression( ExpName::createExpression( this, owner, person->getName() ) );
        }
    }
}



// CatFatPeople ::



CatFatPeople::CatFatPeople( CollectionSubjects &people )
{
    // fill in the collection
    CollectionSubjects::iterator is = people.begin(), is_end = people.end();
    for( ; is != is_end; ++is )
    {
        Person *person = static_cast <Person*> (*is);
        if( person->isFat() )
            m_Subjects.push_back( person );
    }
    if(!empty())
    {
        ostringstream oss;
        oss << (one() ? "the fat guy here" : "the fat guys here");
        m_Text = oss.str();
    }
}



Category *CatFatPeople::createCategory( CollectionSubjects &people )
{
    Category *cat = new CatFatPeople( people );
    if( cat->empty() || cat->getCount() == people.size() )
    {
        delete cat;
        return NULL;
    }
    else
        return cat;
}



void CatFatPeople::generate( CollectionSubjects &subs, Subject *owner, uint8_t level )
{
    if( ExpAllTellTruth::complexity() <= level )
        owner->addExpression( ExpAllTellTruth::createExpression( this, owner ) );
        
    if( ExpAllLie::complexity() <= level )
        owner->addExpression( ExpAllLie::createExpression(this, owner) );
    
    if( ExpAllHumans::complexity() <= level )
        owner->addExpression( ExpAllHumans::createExpression(this, owner) );
        
    if( ExpAllWizards::complexity() <= level )
        owner->addExpression( ExpAllWizards::createExpression(this, owner) );
    
    if( ExpAllVampires::complexity() <= level )
        owner->addExpression( ExpAllVampires::createExpression(this, owner) );
    
    if( ExpSomeHumans::complexity() <= level )
        owner->addExpression( ExpSomeHumans::createExpression(this, owner) );
        
    if( ExpSomeWizards::complexity() <= level )
        owner->addExpression( ExpSomeWizards::createExpression(this, owner) );
    
    if( ExpSomeVampires::complexity() <= level )
        owner->addExpression( ExpSomeVampires::createExpression(this, owner) );
    
    if( ExpName::complexity() <= level )
    {
        CollectionSubjects::iterator ip = subs.begin(), ip_end = subs.end();
        for( ; ip != ip_end; ++ip )
        {
            Person *person = static_cast <Person*> (*ip);
            owner->addExpression( ExpName::createExpression( this, owner, person->getName() ) );
        }
    }
}



// CatSlimPeople ::



CatSlimPeople::CatSlimPeople( CollectionSubjects &people )
{
    // fill in the collection
    CollectionSubjects::iterator is = people.begin(), is_end = people.end();
    for( ; is != is_end; ++is )
    {
        Person *person = static_cast <Person*> (*is);
        if( !person->isFat() )
            m_Subjects.push_back( person );
    }
    if(!empty())
    {
        ostringstream oss;
        oss << (one() ? "the slim guy here" : "the slim guys here");
        m_Text = oss.str();
    }
}



Category *CatSlimPeople::createCategory( CollectionSubjects &people )
{
    Category *cat = new CatSlimPeople( people );
    if( cat->empty() || cat->getCount() == people.size() )
    {
        delete cat;
        return NULL;
    }
    else
        return cat;
}



// CatIncognitoPeople ::



CatIncognitoPeople::CatIncognitoPeople( CollectionSubjects &people )
{
    // fill in the collection
    CollectionSubjects::iterator is = people.begin(), is_end = people.end();
    for( ; is != is_end; ++is )
    {
        Person *person = static_cast <Person*> (*is);
        if( person->isIncognito() )
            m_Subjects.push_back( person );
    }
    if(!empty())
    {
        ostringstream oss;
        oss << (one() ? "the incognito guy here" : "the incognito guys here");
        m_Text = oss.str();
    }
}



Category *CatIncognitoPeople::createCategory( CollectionSubjects &people )
{
    Category *cat = new CatIncognitoPeople( people );
    if( cat->empty() || cat->getCount() == people.size() )
    {
        delete cat;
        return NULL;
    }
    else
        return cat;
}



// CatAllPeople ::



CatAllPeople::CatAllPeople( CollectionSubjects &people )
    : CatAll( people )
{
    m_Text = "the guys here";
}



Category *CatAllPeople::createCategory( CollectionSubjects &people )
{
    if( people.size() > 1 )
        return new CatAllPeople( people ); 
    else
        return NULL;
}



void CatAllPeople::generate( CollectionSubjects &subs, Subject *owner, uint8_t level )
{
    if( ExpAllTellTruth::complexity() <= level )
        owner->addExpression( ExpAllTellTruth::createExpression( this, owner ) );
        
    if( ExpAllLie::complexity() <= level )
        owner->addExpression( ExpAllLie::createExpression(this, owner) );
    
    if( ExpAllHumans::complexity() <= level )
        owner->addExpression( ExpAllHumans::createExpression(this, owner) );
        
    if( ExpAllWizards::complexity() <= level )
        owner->addExpression( ExpAllWizards::createExpression(this, owner) );
    
    if( ExpAllVampires::complexity() <= level )
        owner->addExpression( ExpAllVampires::createExpression(this, owner) );

    if( ExpSomeHumans::complexity() <= level )
        owner->addExpression( ExpSomeHumans::createExpression(this, owner) );
        
    if( ExpSomeWizards::complexity() <= level )
        owner->addExpression( ExpSomeWizards::createExpression(this, owner) );
    
    if( ExpSomeVampires::complexity() <= level )
        owner->addExpression( ExpSomeVampires::createExpression(this, owner) );
    
    // I have excluded the ExpName, because it immediately tells if the owner tells truth or lies 
}



// CatName ::



CatName::CatName( CollectionSubjects &people, Subject *person )
    : CatMutable(people)
{
    m_Text = static_cast <Person *> (person)->getName();
    refill();
}



Category *CatName::createCategory( CollectionSubjects &people, Subject *person )
{
    return new CatName(people, person);
}



bool CatName::empty(Subject *owner)
{
    return false;
}



bool CatName::one(Subject *owner)
{
    return true;
}



uint8_t CatName::getCount(Subject *owner)
{
    return 1;
}



void CatName::refill()
{
    m_Subjects.clear();
    
    CollectionSubjects::iterator is = m_pBaseSubjects->begin(), is_end = m_pBaseSubjects->end();
    for( ; is != is_end; ++is )
    {
        Person *person = static_cast <Person*> (*is);
        if( person->getName() == m_Text )
        {
            m_Subjects.push_back( person );
            break;
        }
    }
}



void CatName::generate( CollectionSubjects &subs, Subject *owner, uint8_t level )
{
    // the owner does not talk about himself
    if( static_cast <Person*> (owner)->getName() == m_Text )
        return;
    
    const uint8_t complexity = 30;
    
    if( complexity + ExpAllTellTruth::complexity() <= level )
        owner->addExpression( ExpAllTellTruth::createExpression( this, owner ) );
    
    if( complexity + ExpAllHumans::complexity() <= level )
        owner->addExpression( ExpAllHumans::createExpression(this, owner) );
        
    if( complexity + ExpAllWizards::complexity() <= level )
        owner->addExpression( ExpAllWizards::createExpression(this, owner) );
    
    if( complexity + ExpAllVampires::complexity() <= level )
        owner->addExpression( ExpAllVampires::createExpression(this, owner) );
 
    if( complexity + ExpAllFat::complexity() <= level )
        owner->addExpression( ExpAllFat::createExpression(this, owner) );
    
    if( complexity + ExpAllIncognito::complexity() <= level )
        owner->addExpression( ExpAllIncognito::createExpression(this, owner) );
}



// CatType ::



CatType::CatType( CollectionSubjects &people, PersonType::eType type )
    : CatMutable(people)
    , m_PersonType(type)
{
    refill();
    ostringstream oss;
    oss << m_PersonType.getText() << "s here";
    m_Text = oss.str();
}



Category *CatType::createCategory( CollectionSubjects &people, PersonType::eType type )
{
    if( people.size() <= 1 )
        return NULL;
    
    Category *cat = new CatType( people, type );
    if( cat->empty() )
    {
        delete cat;
        return NULL;
    }
    else
        return cat;
}



void CatType::refill()
{
    m_Subjects.clear();
    
    CollectionSubjects::iterator is = m_pBaseSubjects->begin(), is_end = m_pBaseSubjects->end();
    for( ; is != is_end; ++is )
    {
        Person *person = static_cast <Person*> (*is);
        if( person->getType() == m_PersonType )
            m_Subjects.push_back( person );
    }    
}



void CatType::generate( CollectionSubjects &subs, Subject *owner, uint8_t level )
{
    const uint8_t complexity = 20;
    
    if( m_PersonType == PersonType::HUMAN )
    {
        if( complexity + ExpAllTellTruth::complexity() <= level )
            owner->addExpression( ExpAllTellTruth::createExpression( this, owner ) );
        
        if( complexity + ExpAllLie::complexity() <= level )
            owner->addExpression( ExpAllLie::createExpression(this, owner) );
    }
   
    if( ExpName::complexity() <= level )
    {
        CollectionSubjects::iterator ip = subs.begin(), ip_end = subs.end();
        for( ; ip != ip_end; ++ip )
        {
            Person *person = static_cast <Person*> (*ip);
            owner->addExpression( ExpName::createExpression( this, owner, person->getName() ) );
        }
    }

    if( complexity + ExpAllFat::complexity() <= level )
        owner->addExpression( ExpAllFat::createExpression(this, owner) );
    
    if( complexity + ExpAllSlim::complexity() <= level )
        owner->addExpression( ExpAllSlim::createExpression(this, owner) );

    if( complexity + ExpAllIncognito::complexity() <= level )
        owner->addExpression( ExpAllIncognito::createExpression(this, owner) );
}



// CatHonestPeople ::



CatHonestPeople::CatHonestPeople( CollectionSubjects &people )
    : CatTruth(people)
{
    if(!empty())
        m_Text = "the honest guys here";
}



Category *CatHonestPeople::createCategory( CollectionSubjects &people )
{
    if( people.size() <= 1 )
        return NULL;
 
    Category *cat = new CatHonestPeople(people);
    if( cat->empty() )
    {
        delete cat;
        return NULL;
    }
    else
        return cat;    
}



void CatHonestPeople::generate( CollectionSubjects &subs, Subject *owner, uint8_t level )
{
    const uint8_t complexity = 30;
    
    if( complexity + ExpName::complexity() <= level )
    {
        CollectionSubjects::iterator ip = subs.begin(), ip_end = subs.end();
        for( ; ip != ip_end; ++ip )
        {
            Person *person = static_cast <Person*> (*ip);
            owner->addExpression( ExpName::createExpression( this, owner, person->getName() ) );
        }
    }
    if( complexity + ExpAllFat::complexity() <= level )
        owner->addExpression( ExpAllFat::createExpression(this, owner) );
    
    if( complexity + ExpAllSlim::complexity() <= level )
        owner->addExpression( ExpAllSlim::createExpression(this, owner) );
    
    if( complexity + ExpAllIncognito::complexity() <= level )
        owner->addExpression( ExpAllIncognito::createExpression(this, owner) );

    if( complexity + ExpAllHumans::complexity() <= level )
        owner->addExpression( ExpAllHumans::createExpression(this, owner) );
        
    if( complexity + ExpAllWizards::complexity() <= level )
        owner->addExpression( ExpAllWizards::createExpression(this, owner) );
    
    if( complexity + ExpSomeHumans::complexity() <= level )
        owner->addExpression( ExpSomeHumans::createExpression(this, owner) );
        
    if( complexity + ExpSomeWizards::complexity() <= level )
        owner->addExpression( ExpSomeWizards::createExpression(this, owner) );    
}



// CatLyingPeople ::



CatLyingPeople::CatLyingPeople( CollectionSubjects &people )
    : CatLying(people)
{
    if(!empty())
        m_Text = "the liars here";
}



Category *CatLyingPeople::createCategory( CollectionSubjects &people )
{
    if( people.size() <= 1 )
        return NULL;
 
    Category *cat = new CatLyingPeople(people);
    if( cat->empty() )
    {
        delete cat;
        return NULL;
    }
    else
        return cat;
}



void CatLyingPeople::generate( CollectionSubjects &subs, Subject *owner, uint8_t level )
{
    const uint8_t complexity = 25;
    
    if( complexity + ExpName::complexity() <= level )
    {
        CollectionSubjects::iterator ip = subs.begin(), ip_end = subs.end();
        for( ; ip != ip_end; ++ip )
        {
            Person *person = static_cast <Person*> (*ip);
            owner->addExpression( ExpName::createExpression( this, owner, person->getName() ) );
        }
    }
    if( complexity + ExpAllFat::complexity() <= level )
        owner->addExpression( ExpAllFat::createExpression(this, owner) );
    
    if( complexity + ExpAllSlim::complexity() <= level )
        owner->addExpression( ExpAllSlim::createExpression(this, owner) );
    
    if( complexity + ExpAllIncognito::complexity() <= level )
        owner->addExpression( ExpAllIncognito::createExpression(this, owner) );

    if( complexity + ExpAllHumans::complexity() <= level )
        owner->addExpression( ExpAllHumans::createExpression(this, owner) );
        
    if( complexity + ExpAllVampires::complexity() <= level )
        owner->addExpression( ExpAllVampires::createExpression(this, owner) );

    if( complexity + ExpSomeHumans::complexity() <= level )
        owner->addExpression( ExpSomeHumans::createExpression(this, owner) );
        
    if( complexity + ExpSomeVampires::complexity() <= level )
        owner->addExpression( ExpSomeVampires::createExpression(this, owner) );
}
